package com.bonc.vectorspace.model;

import java.util.HashMap;
import java.util.Set;

/**
 * @author donggui@bonc.com.cn
 * @version 2016 2016年6月27日 上午11:45:18
 */
public class VectorSpaceModel {

	private EventCorpus corpus;
	
	private Set<String> terms;

	private HashMap<EventDocument, HashMap<String, Double>> tfIdfWeights;

	public VectorSpaceModel(EventCorpus corpus) {
		this.corpus = corpus;
		tfIdfWeights = new HashMap<EventDocument, HashMap<String, Double>>();
		
		createTfIdfWeights();
	}
	
	
	public VectorSpaceModel(EventCorpus corpus, Set<String> terms) {
		this.corpus = corpus;
		this.terms = terms;
		tfIdfWeights = new HashMap<EventDocument, HashMap<String, Double>>();
		
		createTfIdfWeights();
	}

	private void createTfIdfWeights() {
		System.out.println("Creating the tf-idf weight vectors");
//		if()
//		Set<String> terms = corpus.getInvertedIndex().keySet();
		if(terms == null){
			terms = corpus.getInvertedIndex().keySet();
		}
		
		for (EventDocument document : corpus.getDocuments()) {
			HashMap<String, Double> weights = new HashMap<String, Double>();
			
			for (String term : terms) {
				double tf = document.getTermFrequency(term);
				double idf = corpus.getInverseDocumentFrequency(term);
				
				double weight = tf * idf;
				
				weights.put(term, weight);
			}
			tfIdfWeights.put(document, weights);
		}
	}

	public HashMap<EventDocument, HashMap<String, Double>> getTfIdfWeights() {
		return tfIdfWeights;
	}
	
	public EventCorpus getCorpus() {
		return corpus;
	}
	
	private double getMagnitude(EventDocument document) {
		double magnitude = 0;
		HashMap<String, Double> weights = tfIdfWeights.get(document);
		
		for (double weight : weights.values()) {
			magnitude += weight * weight;
		}
		
		return Math.sqrt(magnitude);
	}
	
	private double getDotProduct(EventDocument d1, EventDocument d2) {
		double product = 0;
		HashMap<String, Double> weights1 = tfIdfWeights.get(d1);
		HashMap<String, Double> weights2 = tfIdfWeights.get(d2);
		
		for (String term : weights1.keySet()) {
			product += weights1.get(term) * weights2.get(term);
		}
		
		return product;
	}
	

	public double cosineSimilarity(EventDocument d1, EventDocument d2) {
		return getDotProduct(d1, d2) / (getMagnitude(d1) * getMagnitude(d2));
	}
	
	
	private double getSquare(EventDocument document) {
		double square = 0;
		HashMap<String, Double> weights = tfIdfWeights.get(document);
		
		for (double weight : weights.values()) {
			square += weight * weight;
		}
		
		return square;
	}
	
	public double jaccardSimilarity(EventDocument d1, EventDocument d2){
		double dot = getDotProduct(d1, d2);
		return dot / (getSquare(d1) + getSquare(d2) - dot);
	}

	public Set<String> getTerms() {
		return terms;
	}

	public void setTerms(Set<String> terms) {
		this.terms = terms;
	}
}
